package com.njzxw.fs.commonutil.db;

import com.njzxw.fs.commonutil.annotation.ManagerOrgWhere;
import com.njzxw.fs.commonutil.annotation.ValidInsert;
import com.njzxw.fs.commonutil.annotation.ValidUpdate;
import com.njzxw.fs.commonutil.util.StringHelper;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 动态sql语句封装
 */
public class ZxwLangDriver extends XMLLanguageDriver implements LanguageDriver {

    public org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {

        String type = parameterType.getSimpleName().toLowerCase();

        if(type.equals("parammap")){
            return super.createSqlSource(configuration, script, parameterType);
        }



        String sql = script.toLowerCase().trim();

        if(sql.startsWith("select")){

            script = manageOrg(configuration, script, parameterType);

            script = manageWhereOrg(configuration, script, parameterType);

            script = disposeNull(configuration, script, parameterType);
            /**
             * 处理in标签
             */
            script = disposeIn(configuration, script, parameterType);



        }else if(sql.startsWith("update")){
//            logger.debug("===============");
            script = manageOrg(configuration, script, parameterType);
            /**
             * 处理in标签
             */
            script = disposeIn(configuration, script, parameterType);

            script = disposeSet(configuration, script, parameterType);

        }else if(sql.startsWith("insert")){

            script = disposeInsert(configuration, script, parameterType);

        }else if(sql.startsWith("delete")){

            /**
             * 处理in标签
             */
            script = disposeIn(configuration, script, parameterType);

        }else if(sql.startsWith("drop")){

        }

//        logger.debug("sql===>:"+script);

        return super.createSqlSource(configuration, "<script>" + script + "</script>", parameterType);
    }

    public String manageWhereOrg(Configuration configuration, String script, Class<?> parameterType){

        //根据第几个where解析生成组织权限判断
        Matcher matcher = Pattern.compile("(\\s*)([wW][Hh][Ee][Rr][Ee])(\\s*)(1[\\=]1)(\\s*)").matcher(script);
        if (matcher.find()) {
            script = matcher.replaceAll(" \\$\\{manage_org_table_name\\} where 1=1 \\$\\{manage_org_table_name_where\\} ");
        }
        return script;
    }

    /**
     * 进行组织角色限制
     *
     * @param configuration
     * @param script
     * @param parameterType {#manage_org}
     */
    public String manageOrg(Configuration configuration, String script, Class<?> parameterType){
//        logger.debug("script:"+script);
        Matcher matcher = Pattern.compile("\\{#manage_org\\}").matcher(script);
        matcher.reset();
        StringBuffer sb = new StringBuffer();
        int cnt = 0;
        int end = 0;
        while (matcher.find()) {
            end = matcher.end();
            String str = matcher.group();
            String tmp = "";
            if(cnt == 0){
                matcher.appendReplacement(sb, "\\$\\{manage_org_table_name\\}");
            }else{
                matcher.appendReplacement(sb, "\\$\\{manage_org_table_name_where\\}");
            }
            cnt ++;
        }
        if(end != 0){
            sb.append(script.substring(end));
        }
        return sb.length()>0?sb.toString():script;
    }

    public String disposeInsert(Configuration configuration, String script, Class<?> parameterType){
//        logger.debug("script:"+script);
        Matcher matcher = Pattern.compile("\\(#\\{(\\w+)\\}\\)").matcher(script);

        if (matcher.find()) {
            StringBuilder sb = new StringBuilder();
            StringBuilder tmp = new StringBuilder();
            sb.append("(");


            Field[] fields = FieldUtils.getFieldsWithAnnotation(parameterType,ValidInsert.class);
            for (int i=0;i<fields.length;i++) {
                Field field = fields[i];
                    sb.append("`"+StringHelper.camelToUnderline(field.getName()) + "`,");

//                    logger.debug("field.getType().getSimpleName():"+field.getName()+"===="+field.getType().getSimpleName());
                    if("int".equals(field.getType().getSimpleName())||"Integer".equals(field.getType().getSimpleName())){
                        if(i == fields.length-1){
                            tmp.append("<choose><when test=\""+field.getName()+" == null\" >null</when><when test=\""+field.getName()+" != " +"null\" >#{"+field.getName()+"}</when></choose>");
                        }else{
                            tmp.append("<choose><when test=\""+field.getName()+" == null\" >null,</when><when test=\""+field.getName()+" != " +"null\" >#{"+field.getName()+"},</when></choose>");
                        }
                    }else{
                        if(i == fields.length-1){
                            tmp.append("<choose><when test=\""+field.getName()+" == '' \" >null</when><when test=\""+field.getName()+" != " +"''\" >#{"+field.getName()+"}</when></choose>");
                        }else{
                            tmp.append("<choose><when test=\""+field.getName()+" == '' \" >null,</when><when test=\""+field.getName()+" != " +"''\" >#{"+field.getName()+"},</when></choose>");
                        }
                    }

            }

            sb.deleteCharAt(sb.lastIndexOf(","));
//            tmp.deleteCharAt(tmp.lastIndexOf(","));
            sb.append(") values (" + tmp.toString() + ")");

            script = matcher.replaceAll(sb.toString());
        }
        return script;

    }

    public String disposeSet(Configuration configuration, String script, Class<?> parameterType){
//        logger.debug("script:"+script);
        Matcher matcher = Pattern.compile("\\(#\\{(\\w+)\\}\\)").matcher(script);

        if (matcher.find()) {
            StringBuilder sb = new StringBuilder();
            sb.append("<set>");

            for (Field field : parameterType.getDeclaredFields()) {
                if(checkFieldType(field) && field.isAnnotationPresent(ValidUpdate.class)){
                    String tmp = "<if test=\"_field != null\"><choose><when test=\"_field == ''\" >_column = null,</when><when test=\"_field != ''\" >_column=#{_field},</when></choose></if>";
//                    String tmp = "<choose><when test=\"_field == ''\" >_column = null,</when><when test=\"_field != ''\" >_column=#{_field},</when></choose>";
                    sb.append(tmp.replaceAll("_field", field.getName()).replaceAll("_column",
                            StringHelper.camelToUnderline(field.getName())));
                }
            }

//            sb.deleteCharAt(sb.lastIndexOf(","));
            sb.append("</set>");

            script = matcher.replaceAll(sb.toString());
        }
        return script;

    }

    public boolean checkFieldType(Field field){

        String type = field.getType().getSimpleName();

        if("String".equals(type) || "int".equals(type)|| "Integer".equals(type)  || "Data".equals(type) ){
            return true;
        }
        return false;
    }

    public String disposeNull(Configuration configuration, String script, Class<?> parameterType){

        Matcher matcher = Pattern.compile("(\\s*)(([aA][nN][Dd]){0,1})(\\s*)(((\\w+)(\\.{0,1})(\\w+))|((\\w+)\\((((\\w)|(\\s)|(\\.)|(\\,)|(\\')|(\\%)|(\\-))*)\\)))(\\s*)(like|=|(>=)|(<=)|(<>)|>|<)(\\s*)(#|\\$)\\{(\\w+)\\}").matcher(script);
        matcher.reset();
        StringBuffer sb = new StringBuffer();
        int end = 0;
        while (matcher.find()) {
            end = matcher.end();
            String str = matcher.group();
            String fired = getText(str);
            String fied = getTextY(str);
            String fh = getFh(str);
            String sqlFied = getFied(str);
            String sqladd = getAdd(str);
//            logger.debug("===||:"+sqladd + sqlFied +fh+fied);
//            String tmp = " <if test=\""+fired+" != null and "+fired+" != '' \"> <![CDATA[" +sqladd + sqlFied +fh+fied +"]]></if>";
//            String tmp = " <if test=\""+fired+" != null and "+fired+" != '' \"> <![CDATA[" +str +"]]></if>";
            String tmp = " <if test=\""+fired+" != null and "+fired+" != '' \"> <![CDATA[" +str +"]]></if>";
//            logger.debug("tmp:"+tmp);
            matcher.appendReplacement(sb, tmp);

        }
//        logger.debug("====>>>"+end+"==="+script.length());
        if(end != 0){
            sb.append(script.substring(end));
        }

        return sb.length()>0?sb.toString():script;
    }



    /**
     * 处理select IN
     * @param configuration
     * @param script
     * @param parameterType
     * @return
     */
    public String disposeIn(Configuration configuration, String script, Class<?> parameterType){

        Matcher matcher = Pattern.compile("(\\s*)(([aA][nN][Dd]){0,1})(\\s*)(\\w+)(\\.{0,1})(\\w+)(\\s*)([iI][nN])(\\s*)\\(#\\{(\\w+)\\}\\)").matcher(script);
        matcher.reset();
        StringBuffer sb = new StringBuffer();
        int end = 0;
        while (matcher.find()) {
            end = matcher.end();
            String str = matcher.group();
            String fired = getText(str);
            String sqlFied = getFied(str);
            String sqladd = getAdd(str);
//            logger.debug(fired+"=="+sqlFied+"=="+sqladd);
            String tmp = " <if test=\""+fired+" != null \"> " +
                    sqladd + sqlFied +
                    " in <foreach collection=\""+fired+"\" item=\"_item\" open=\"(\" " +
                    "separator=\",\" close=\")\" >#{_item}</foreach>"+
                    "</if>";

            matcher.appendReplacement(sb, tmp);

        }

        if(end != 0){
            sb.append(script.substring(end));
        }

        return sb.length()>0?sb.toString():script;
    }

    /**
     * 获取字段名 不包含{}
     *
     * @param text
     * @return
     */
    public String getFh(String text){

        Matcher matcher = Pattern.compile("like|>=|(<=)|(<>)|(=)|>|<").matcher(text);
        if(matcher.find()){
            return matcher.group();
        }
        return "";

    }

    /**
     * 获取字段名 不包含{}
     *
     * @param text
     * @return
     */
    public String getText(String text){

        Matcher matcher = Pattern.compile("\\{(\\w+)\\}").matcher(text);
        if(matcher.find()){
            return matcher.group().replaceAll("\\{|\\}","");
        }
        return "";

    }

    /**
     * 获取字段名 包含{}
     *
     * @param text
     * @return
     */
    public String getTextY(String text){

        Matcher matcher = Pattern.compile("(#|\\$)\\{(\\w+)\\}").matcher(text);
        if(matcher.find()){
            return matcher.group();
        }
        return "";

    }

    /**
     * 获取and
     * @param text
     * @return
     */
    public String getAdd(String text){
        Matcher matcher = Pattern.compile("(\\s*)(([aA][nN][Dd]){0,1})(\\s*)").matcher(text);
        if(matcher.find()){
            return matcher.group();
        }
        return "";
    }

    /**
     * 获取字段
     * @param text
     * @return
     */
    public String getFied(String text){
        Matcher matcher = Pattern.compile("(\\s*)(\\w+)(\\.{0,1})(\\w+)(\\s*)").matcher(text);
        while(matcher.find()){
            String fied = matcher.group();
//            logger.debug("fied:"+fied);
            if(!"and".equals(fied.trim().toLowerCase())){
                return fied;
            }
        }
        return "";
    }

}
